/**
 * 
 */
package ro.dta.idbi.ui;

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import ro.dta.idbi.interfaces.ITransformationResult;
import ro.dta.idbi.interfaces.ui.IExecTransDialog;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.model.AbstractPattern;
import ro.dta.idbi.model.TransformationException;
import ro.dta.idbi.model.TransformationResult;
import ro.dta.idbi.model.dynamicpattern.DependencyGraph;
import ro.dta.idbi.model.dynamicpattern.GraphHash;
import ro.dta.idbi.model.dynamicpattern.ParseException;
import ro.dta.idbi.model.dynamicpattern.Transaction;
import ro.dta.idbi.model.dynamicpattern.TransactionException;
import ro.dta.idbi.model.dynamicpattern.VFIsomorphism;
import uk.ac.ic.doc.automed.DSRException;
import uk.ac.ic.doc.automed.NotFoundException;
import uk.ac.ic.doc.automed.reps.Schema;

/**
 * Dialog for allowing the user to execute any number of transformation patterns
 * 
 * @author Tudor Dobrila
 * 
 */
public class ExecTransDialog extends JDialog implements IExecTransDialog {

	private static final long serialVersionUID = -4358580242298383243L;

	/**
	 * Minimum number of identical transactions for the tool to prompt the user
	 * with a message to view other matching transactions
	 */
	private static final int MATCHES_THRESHOLD = 3;

	/**
	 * Schema after the execution of the transformations
	 */
	private ITransformationResult result;

	/**
	 * Parent frame of the application
	 */
	private final IMainFrame parentFrame;

	/**
	 * Execute transformation dialog class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of the application
	 * @param schema
	 *            Schema over which the transformations are applied
	 */
	public ExecTransDialog(final IMainFrame parentFrame, final Schema schema,
			final boolean isTransaction) {
		this.parentFrame = parentFrame;
		setSize(800, 500);
		setTitle("Execute " + (isTransaction ? "transaction" : "transformation patterns")
				+ " over schema " + schema);
		setModal(true);
		setLocationRelativeTo(null);
		setIconImage(parentFrame.getIconImage());

		final JPanel mainPanel = new JPanel();
		mainPanel.setLayout(new GridBagLayout());
		mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		getContentPane().add(mainPanel, BorderLayout.CENTER);

		final GridBagConstraints constr = new GridBagConstraints();
		constr.fill = GridBagConstraints.BOTH;

		final LineTextArea lineTextArea = new LineTextArea();
		constr.weightx = 1.0;
		constr.weighty = 0.3;
		constr.gridx = 0;
		constr.gridy = 0;
		mainPanel.add(lineTextArea, constr);

		final JButton executeBtn = new JButton("Execute "
				+ (isTransaction ? "transaction" : "commands"));
		executeBtn.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(final ActionEvent event) {
				String[] commands = lineTextArea.getTextArea().getText().split("\n");
				try {
					Schema finalSchema = null;
					if (isTransaction) {
						Transaction transaction = new Transaction(new GraphHash("MD5"), commands,
								schema.getName());
						transaction.execute();

						// Retrieve similar transactions
						List<Integer> matches = getIdenticalTransactionsCount(transaction);
						transaction.save();

						finalSchema = Schema.getSchema(transaction.getFinalSchema());

						if (matches.size() >= MATCHES_THRESHOLD) {
							openSimilarTransDialog(transaction, matches);
						}
					} else {
						finalSchema = AbstractPattern.execute(schema, commands);
					}

					parentFrame.getRepositoryBrowser().refresh();
					parentFrame.getRepositoryBrowser().select(finalSchema.getName());
					parentFrame
							.setStatusBarText("Transformation applied successfully. New schema: "
									+ finalSchema.getName());
					setVisible(false);
					result = new TransformationResult(schema, finalSchema, commands);
				} catch (TransformationException e) {
					JOptionPane.showMessageDialog(ExecTransDialog.this,
							"Could not execute the commands.\r\nReason: " + e.getMessage(),
							"Error", JOptionPane.ERROR_MESSAGE);
				} catch (ParseException e) {
					JOptionPane.showMessageDialog(ExecTransDialog.this,
							"Could not execute the transaction.\r\nReason: " + e.getMessage(),
							"Error", JOptionPane.ERROR_MESSAGE);
				} catch (DSRException e) {
					JOptionPane.showMessageDialog(ExecTransDialog.this,
							"Could not insert the transaction in the AutoMed repository.\r\nReason: "
									+ e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
				} catch (IOException e) {
					JOptionPane.showMessageDialog(
							ExecTransDialog.this,
							"Could not encode the signature of the transaction.\r\nReason: "
									+ e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
				} catch (SQLException e) {
					JOptionPane.showMessageDialog(ExecTransDialog.this,
							"Could not store the transaction in the transaction history.\r\nReason: "
									+ e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
				} catch (TransactionException e) {
					JOptionPane.showMessageDialog(ExecTransDialog.this,
							"Could not execute transaction.\r\nReason: " + e.getMessage(), "Error",
							JOptionPane.ERROR_MESSAGE);
				} catch (NotFoundException e) {
					JOptionPane.showMessageDialog(ExecTransDialog.this,
							"Could not final final schema, obtained after the application of the transaction.\r\nReason: "
									+ e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
				} catch (NoSuchAlgorithmException e) {
					JOptionPane
							.showMessageDialog(
									ExecTransDialog.this,
									"Could not initialise the hash algorithm.\r\nReason: "
											+ e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
				} catch (ClassNotFoundException e) {
					JOptionPane.showMessageDialog(ExecTransDialog.this,
							"Could not load similar transactions from the transaction history.\r\nReason: "
									+ e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
				}

			}
		});
		constr.gridx = 0;
		constr.gridy = 1;
		constr.weightx = 1.0;
		constr.weighty = 0.0;
		constr.insets = new Insets(3, 3, 3, 3);
		constr.fill = GridBagConstraints.NONE;
		mainPanel.add(executeBtn, constr);
	}

	/**
	 * Open a dialog with similar transactions
	 * 
	 * @param transaction
	 *            Original transaction
	 * @param matches
	 *            List of transaction that match the original transaction
	 */
	private void openSimilarTransDialog(Transaction transaction, List<Integer> matches) {
		String confMsg = "Another "
				+ matches.size()
				+ " similar transaction(s) exist in the transaction history. Would you like to view them?";
		int result = JOptionPane.showConfirmDialog(ExecTransDialog.this, confMsg,
				"Similar transactions", JOptionPane.YES_NO_OPTION);
		if (result == JOptionPane.YES_OPTION) {
			TransactionHistoryDialog thd;
			try {
				thd = new TransactionHistoryDialog(parentFrame, matches);
				thd.setVisible(true);
			} catch (Exception e) {
				JOptionPane
						.showMessageDialog(
								ExecTransDialog.this,
								"Could not open list of similar transactions.\r\nReason: "
										+ e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
			}
		}
	}

	/**
	 * Get the transactions from the history identical to this one
	 * 
	 * @param transaction
	 *            Transaction for which to check for
	 * @return List of transactions similar to this one
	 */
	private List<Integer> getIdenticalTransactionsCount(Transaction transaction)
			throws DSRException, ClassNotFoundException, IOException, SQLException {
		List<Integer> result = new ArrayList<Integer>();
		List<Transaction> similarTrans = Transaction.loadTransactions(transaction.getHashCode());
		VFIsomorphism graphIso = new VFIsomorphism();

		for (Transaction trans : similarTrans) {
			if (DependencyGraph.matches(transaction.getSignature(), trans.getSignature(), graphIso)) {
				result.add(trans.getTid());
			}
		}

		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.ui.IExecTransDialog#getTransformationResult()
	 */
	@Override
	public ITransformationResult getTransformationResult() {
		return result;
	}

}
